﻿using Microsoft.Phone.Maps.Controls;
using Microsoft.Phone.Maps.Services;
using System;
using System.Collections.Generic;
using System.Device.Location;
using System.Linq;
using System.Windows;

namespace INdTFramework.Maps
{
    /// <summary>
    /// Specialized class to manipulate some features of the map control.
    /// </summary>
    public class HelperMap
    {
        private MapLayer layer;

        /// <summary>
        /// Gets or sets the map control.
        /// </summary>
        public Map Map { get; set; }

        /// <summary>
        /// Method constructor of the class.
        /// </summary>
        /// <param name="map">The instance of the map control that will be handled.</param>
        public HelperMap(Map map)
        {
            this.Map = map;
            this.layer = new MapLayer();
        }

        /// <summary>
        /// Adds a marker on the map control that was passed to the constructor method.
        /// </summary>
        /// <param name="overlay">Instance that contains the properties of the marker to be added.</param>
        public void AddMarker(MapOverlay overlay)
        {
            layer.Add(overlay);

            if (Map.Layers.Count == 0)
            {
                Map.Layers.Add(layer);
            }
        }

        /// <summary>
        /// Adds a marker on the map control that was passed to the constructor method.
        /// </summary>
        /// <param name="coordinates">The coordinates of the marker.</param>
        /// <param name="content">The visual representation of the marker.</param>
        /// <param name="positionOrigin">The marker position in relation its coordinates on the map control.</param>
        public void AddMarker(GeoCoordinate coordinates, object content, Point positionOrigin)
        {
            var overlay = new MapOverlay();
            overlay.GeoCoordinate = coordinates;
            overlay.Content = content;
            overlay.PositionOrigin = positionOrigin;

            layer.Add(overlay);

            if (Map.Layers.Count == 0)
            {
                Map.Layers.Add(layer);
            }
        }

        /// <summary>
        /// Adds a list of markers on the map control that was passed to the constructor method.
        /// </summary>
        /// <param name="overlays">List of markers.</param>
        public void AddMarkers(List<MapOverlay> overlays)
        {
            foreach (var overlay in overlays)
            {
                layer.Add(overlay);
            }

            if (Map.Layers.Count == 0)
            {
                Map.Layers.Add(layer);
            }
        }

        /// <summary>
        /// Removes a marker that is on the map control.
        /// </summary>
        /// <param name="overlay">Object that contains the information of the marker to be removed.</param>
        public void RemoveMarker(MapOverlay overlay)
        {
            layer.Remove(overlay);
        }

        /// <summary>
        /// Removes a marker that is on the map control.
        /// </summary>
        /// <param name="coordinates">Coordinates of the marker to be removed.</param>
        public void RemoveMarker(GeoCoordinate coordinates)
        {
            var overlays = layer.Where(o => o.GeoCoordinate.Equals(coordinates)).ToList();

            foreach (var overlay in overlays)
            {
                layer.Remove(overlay);
            }
        }

        /// <summary>
        /// Remove all the markers that are on the map control that was passed to the constructor method.
        /// </summary>
        public void RemoveAllMarkers()
        {
            layer.Clear();
        }

        /// <summary>
        /// Gets the route between geographical points.
        /// </summary>
        /// <param name="listCoordinates">List of coordinates of the geographical points.</param>
        /// <param name="action">Action to get an object of type Route containing the route information and to manipulate that object.</param>
        /// <param name="travelMode">Mode of travel for the route.</param>
        /// <param name="routeOptimization">Route optimization.</param>
        public void TraceRoute(List<GeoCoordinate> listCoordinates, Action<Route> action, TravelMode travelMode = TravelMode.Driving, RouteOptimization routeOptimization = RouteOptimization.MinimizeDistance)
        {
            var routeQuery = new RouteQuery();
            routeQuery.Waypoints = listCoordinates;
            routeQuery.TravelMode = travelMode;
            routeQuery.RouteOptimization = routeOptimization;
            routeQuery.QueryCompleted += new EventHandler<QueryCompletedEventArgs<Route>>((sender, e) =>
                {
                    if (e.Error == null)
                    {
                        action(e.Result);
                    }
                });
            routeQuery.QueryAsync();
        }

        /// <summary>
        /// Adds a route on the map control.
        /// </summary>
        /// <param name="route">Object that contains the route information.</param>
        public void AddRoute(Route route)
        {
            var mapRoute = new MapRoute(route);

            Map.AddRoute(mapRoute);
        }


        /// <summary>
        /// Search a location for a given search term, for example a town name or street address.
        /// </summary>
        /// <param name="searchTerm">The search term, which can be the name of a city or a street name.</param>
        /// <param name="beginningCoordinates">Reference coordinates for the search.</param>
        /// <param name="action">Action to get a list of objects of type MapLocation, which contains the information of the sites found, and to manipulate that object.</param>
        public void SearchForTerm(string searchTerm, GeoCoordinate beginningCoordinates, Action<List<MapLocation>> action)
        {
            var geocodeQuery = new GeocodeQuery();
            geocodeQuery.SearchTerm = searchTerm;
            geocodeQuery.GeoCoordinate = beginningCoordinates;
            geocodeQuery.QueryCompleted += new EventHandler<QueryCompletedEventArgs<IList<MapLocation>>>((sender, e) =>
                {
                    if (e.Error == null)
                    {
                        action(e.Result.ToList());
                    }
                });
            
            geocodeQuery.QueryAsync();
            
        }

        /// <summary>
        /// Sets the midpoint between two coordinates and these new coordinates become the center of the map control that was passed to the constructor method.
        /// </summary>
        /// <param name="beginning">Beginning coordinates.</param>
        /// <param name="destination">Destination coordinates.</param>
        public void SetCenter(GeoCoordinate beginning, GeoCoordinate destination)
        {
            var newLatitude = (beginning.Latitude + destination.Latitude) / 2;
            var newLongitude = (beginning.Longitude + destination.Longitude) / 2;

            Map.Center = new GeoCoordinate(newLatitude, newLongitude);
        }
    }
}
